package com.javaspeak.designpatterns.go4.behavioural.interpreter;

import java.util.ArrayList;
import java.util.List;

/**
 * The interpreter class is responsible for holding a reference to the
 * InterpreterContext and for determining which Expressions to use for parsing
 * the interpreted language.  The interpreter parses
 * "Object Query Language (oql)" into statements and then executes the
 * statements. Different Expression objects are used for parsing different
 * kinds of statements.
 * <p>
 * For example a InsertExpression is used for parsing oql insert statements
 * and a SelectExpression is used for parsing oql select statements.
 * <p>
 * In out example the InterpreterContext registers Java objects and allows us
 * to update their values and select values from them by making method calls.
 * The Expression objects, InsertExpression and SelectExpression make calls to
 * the InterpeterContext to update the registered objects with new values or
 * to retrieve values.
 *
 * @author John Dickerson
 */
public class Interpreter {

    // Context holding registered objects
    private InterpreterContext interpreterContext;


    /**
     * Constructor to pass in the context
     *
     * @param interpreterContext
     */
    public Interpreter( InterpreterContext interpreterContext ){

        this.interpreterContext = interpreterContext;
    }


    /**
     * Performs the following steps:
     * <ul>
     * <li>Parses "Object Query Language (oql)" into statements by looking
     * for ";" separators.
     * <li>Checks the first word of each statement to determine which
     * Expression to use to parse the statement. If a "select" is present uses
     * a SelectExpression. If a "insert" is present uses a InsertExpression.
     * <li>interprets the Expression. The Expression in turn parses the oql
     * and makes calls to the InterpreterContext to either insert data into the
     * registered objects or retrieve data from them.
     *
     * @param oql
     * @return
     * @throws Exception
     */
    public List<Value> interpret( String oql ) throws Exception {

        // separate statements by ";"
        String[] statements = oql.split( ";" );

        // create a list of values to return. Note that multile select
        // statements can be called but only one list of values is returned.
        List<Value> valuesToReturn = new ArrayList<Value>();

        // For each statement choose a Expression to do the work
        for ( String statement : statements ){

            statement = statement.trim();

            AbstractExpression expression;

            // if the statements start with "select" use a SelectExpression to
            // parse statement.  If starts with "insert" use a InsertExpression
            // to parse statement
            if ( statement.toLowerCase().startsWith( "select" ) ) {

                expression = new SelectExpression( statement );

                valuesToReturn.addAll(
                     expression.interpret( interpreterContext ) );
            }
            else if ( statement.toLowerCase().startsWith( "insert" ) ) {

                expression = new InsertExpression( statement );
                expression.interpret( interpreterContext );
            }
            else {

                // The statements starts with an unexpected word so throw
                // an error
                throw new Exception( "Incorrect syntax in : " + statement );
            }
        }

        return valuesToReturn;
    }
}
